package yartiss.engine.simulation.system.taskset.generation.task;

import java.util.ArrayList;

import yartiss.engine.simulation.system.ITask;
import yartiss.engine.simulation.system.schedulable.GraphTask;
import yartiss.engine.simulation.system.schedulable.SchedulableFactory;
import yartiss.engine.simulation.system.schedulable.SubTask;

/**
 * GraphTaskGenerator is a class that extends AbstractTaskGenerator and
 * generates new instance of graph task.
 * 
 * @author Manar Qamhieh
 * 
 */

public class GraphTaskGenerator extends AbstractTaskGenerator {
	private static final int MAX_SUBTASKS = 8;
	private int MAX_PROC;
	private long start;
	private long wcet;
	private long deadline;
	private long period;
	private int priority;
	private GraphTask graph;

	@Override
	protected ITask generate(long start, long wcet, long period, long deadline,
			int priority, int maxProc) {

		this.start = start;
		this.wcet = wcet;
		this.deadline = deadline;
		this.period = period;
		this.MAX_PROC = maxProc;
		this.priority = priority;
		ArrayList<SubTask> subtasks = new ArrayList<SubTask>();
		subtasks = generateSubtasks();
		graph = (GraphTask) SchedulableFactory.newInstance("graph", start,
				period, deadline, priority, subtasks, null);
		// GraphTask graph = new GraphTask(start, 0, period, deadline, priority,
		// subtasks);
		generateRelations(graph);
		graph.calculateCriticalPath();
		return graph;
	}

	/**
	 * Created random edges between the subtasks in a graph
	 * 
	 * @param graph
	 *            target graph
	 */
	private void generateRelations(GraphTask graph) {
		int numOfChildren;
		int listSize = graph.getSubtasks().size();
		for (int i = 0; i < listSize - 1; i++) {
			numOfChildren = nextInt(1, listSize - (i + 1));
			for (int j = 0; j < numOfChildren; j++) {
				int index = nextInt(i + 1, listSize - 1);
				if (graph.canAddEdge(graph.getSubtasks().get(i), graph
						.getSubtasks().get(index))) {
					graph.addEdge(graph.getSubtasks().get(i), graph
							.getSubtasks().get(index));
				}
			}
		}
		verifyEdges(graph);
	}

	/**
	 * Verifies if all subtasks in the graph are connected
	 * 
	 * @param graph
	 *            graph in which subtasks are verified
	 */
	private void verifyEdges(GraphTask graph) {
		int listSize = graph.getSubtasks().size();
		for (int i = 1; i < listSize - 1; i++) {
			if (graph.getSubtasks().get(i).getChildrenTasks().isEmpty())
				graph.addEdge(graph.getSubtasks().get(i), graph.getSubtasks()
						.get(listSize - 1));
			if (graph.getSubtasks().get(i).getParentTasks().isEmpty())
				graph.addEdge(graph.getSubtasks().get(0), graph.getSubtasks()
						.get(i));
		}
		if (graph.getStartingSubtask().getChildrenTasks()
				.contains(graph.getEndingSubtask()) && listSize!=2)
			graph.removeEdge(graph.getStartingSubtask(),
					graph.getEndingSubtask());
	}

	/**
	 * Generates a random list of subtasks with random distribution of the total
	 * execution time and random number of processors
	 * 
	 * @return generated list of subtasks
	 */
	private ArrayList<SubTask> generateSubtasks() {

		ArrayList<SubTask> list = new ArrayList<SubTask>();
		int numOfSubtasks = nextInt(1, MAX_SUBTASKS);
		if (numOfSubtasks == 1 || wcet < numOfSubtasks) {
			SubTask subTask = (SubTask) SchedulableFactory.newInstance(
					"subtask", start, wcet, 0, period, deadline, priority, 1, null);
			// SubTask subTask = new SubTask(start, wcet, 0, period, deadline,
			// priority, 1);
			subTask.setIndex(list);
			list.add(subTask);
			return list;
		}

		int numOfProc;
		long subWcet;
		long rest = wcet;

		for (int i = 0; i < numOfSubtasks; i++) {
			subWcet = nextInt(1, (int) (rest - (numOfSubtasks - i + 1)));
			numOfProc = nextInt(1, MAX_PROC);
			// if ((subWcet % numOfProc == 0) && (i != 0)
			if ((Math.floor(subWcet / numOfProc) > 1) && (i != 0)
					&& (i != numOfSubtasks - 1))
				subWcet = (long) Math.floor(subWcet / numOfProc);
			else
				numOfProc = 1;
			SubTask subTask = (SubTask) SchedulableFactory.newInstance(
					"subtask", start, (subWcet * numOfProc), 0, period,
					deadline, priority, numOfProc, null);
			// SubTask subTask = new SubTask(start, (subWcet * numOfProc), 0,
			// period, deadline, priority, numOfProc);
			subTask.setIndex(list);
			list.add(subTask);
			rest -= (subWcet * numOfProc);
		}
		return list;
	}

}
